package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import com.atguigu.tingshu.album.mapper.AlbumAttributeValueMapper;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.cache.GuiGuCache;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.vo.album.AlbumAttributeValueVo;
import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {

	@Autowired
	private AlbumInfoMapper albumInfoMapper;
    @Autowired
    private AlbumStatMapper albumStatMapper;
    @Autowired
    private AlbumAttributeValueMapper albumAttributeValueMapper;
    @Autowired
    private TrackInfoMapper trackInfoMapper;
    @Autowired
    private VodService vodService;
    @Autowired
    private RabbitService rabbitService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    /**
     * 内容创作者/运营人员保存专辑
     * * TODO 业务校验-验证内容安全
     * * 1.封装专辑相关信息，保存一条记录到专辑信息表
     * * 2.封装专辑标签关系集合，保存若干条记录到专辑标签关系表
     * * 3.封装专辑统计信息，保存4条记录到专辑统计表
     *
     * @param albuminfo 新增专辑信息
     * @param userId    用户ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveAlbumInfo(AlbumInfoVo albumInfoVo, Long userId) {
        //保存专辑信息
        //将vo转成po  hutool
        AlbumInfo albumInfo = BeanUtil.copyProperties(albumInfoVo, AlbumInfo.class);
        albumInfo.setUserId(userId);
        //设置状态为待审核
        albumInfo.setStatus(SystemConstant.ALBUM_STATUS_REVIEWING_PASS);

        //保存专辑标签关系
        this.save(albumInfo);
        Long albumId = albumInfo.getId();

        //保存四条统计信息
        List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
        if (CollectionUtil.isNotEmpty(albumAttributeValueVoList)){
            List<AlbumAttributeValue> albumAttributeValues = albumAttributeValueVoList
                    .stream()
                    .map(albumAttributeValueVo -> BeanUtil.copyProperties(albumAttributeValueVo, AlbumAttributeValue.class))
                    .collect(Collectors.toList());
            albumAttributeValues.forEach(albumAttributeValue -> {
                albumAttributeValue.setAlbumId(albumId);
                albumAttributeValueMapper.insert(albumAttributeValue);
            });
            //执行保存专辑标签关系
            this.saveAlbumStat(albumId, SystemConstant.ALBUM_STAT_PLAY, 0);
            this.saveAlbumStat(albumId, SystemConstant.ALBUM_STAT_SUBSCRIBE, 0);
            this.saveAlbumStat(albumId, SystemConstant.ALBUM_STAT_BUY, 0);
            this.saveAlbumStat(albumId, SystemConstant.ALBUM_STAT_COMMENT, 0);

            //对文本内容进行审核 异步审核
            vodService.reviewAlbumText(albumInfo);
        }
    }



    @Override
    public void saveAlbumStat(Long albumId, String statType, int statNum) {
        AlbumStat albumStat = new AlbumStat();
        albumStat.setAlbumId(albumId);
        albumStat.setStatType(statType);
        albumStat.setStatNum(statNum);
        albumStatMapper.insert(albumStat);
    }

    @Override
    public Page<AlbumListVo> getUserAlbumPage(Page<AlbumListVo> pageInfo, Long userId, AlbumInfoQuery albumInfoQuery) {
        return albumInfoMapper.getUserAlbumPage(pageInfo, userId, albumInfoQuery);
    }
    /**
     * 根据专辑ID删除专辑
     * 1.判断该专辑是否关联声音
     * 2.删除专辑记录
     * 3.删除统计记录
     * 4.删除专辑标签记录
     *
     * @param id
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeAlbumInfo(Long id) {
        //根据专辑ID查询声音数量 判断是否关联声音 如果关联声音则不允许删除，提示错误信息
        Long count = trackInfoMapper.selectCount(
                new LambdaQueryWrapper<TrackInfo>()
                        .eq(TrackInfo::getAlbumId, id)
        );
        if (count > 0){
            throw new GuiguException(500,"该专辑关联声音，不允许删除");
        }
        //删除专辑信息
        albumInfoMapper.deleteById(id);

        //根据专辑id删除统计信息
        albumStatMapper.delete(
                new LambdaQueryWrapper<AlbumStat>()
                        .eq(AlbumStat::getAlbumId, id)
        );

        //根据专辑id删除专辑标签关系
        albumAttributeValueMapper.delete(
                new LambdaQueryWrapper<AlbumAttributeValue>()
                        .eq(AlbumAttributeValue::getAlbumId, id)
        );

        //  通过mq通知专辑服务删除专辑索引
        rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM , MqConst.ROUTING_ALBUM_LOWER , id );
    }

    /**
     * 根据专辑ID查询专辑信息（包括专辑标签列表）
     *
     * @param id 专辑ID
     * @return 专辑信息
     */
    @Override
    @GuiGuCache(prefix = RedisConstant.ALBUM_INFO_PREFIX)
    public AlbumInfo getAlbumInfoFromDB(Long id) {
        // 根据专辑id查询专辑信息
        AlbumInfo albumInfo = albumInfoMapper.selectById(id);
        // 根据专辑id查询专辑标签列表
        List<AlbumAttributeValue> albumAttributeValueList = albumAttributeValueMapper.selectList(
                new LambdaQueryWrapper<AlbumAttributeValue>()
                        .eq(AlbumAttributeValue::getAlbumId, id)
        );
        if (CollectionUtil.isNotEmpty(albumAttributeValueList)){
            albumInfo.setAlbumAttributeValueVoList(albumAttributeValueList);
        }
        return albumInfo;
    }

    /**
     * 更新专辑信息
     * @param albumInfoVo
     */
    @Override
    public void updateAlbumInfo(Long id, AlbumInfoVo albumInfoVo) {

        //更新专辑信息表
        AlbumInfo albumInfo = BeanUtil.copyProperties(albumInfoVo, AlbumInfo.class);
        albumInfo.setId(id);
        baseMapper.updateById(albumInfo);
        //更新专辑标签关系表
        albumAttributeValueMapper.delete(
                new LambdaQueryWrapper<AlbumAttributeValue>()
                        .eq(AlbumAttributeValue::getAlbumId, id)
        );
        //新增专辑标签关系表
        List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
        if (CollectionUtil.isNotEmpty(albumAttributeValueVoList)){
            List<AlbumAttributeValue> albumAttributeValues = albumAttributeValueVoList
                    .stream()
                    .map(albumAttributeValueVo -> {
                                AlbumAttributeValue albumAttributeValue = BeanUtil.copyProperties(albumAttributeValueVo, AlbumAttributeValue.class);
                                albumAttributeValue.setAlbumId(id);
                                return albumAttributeValue;
                            }
                    ).collect(Collectors.toList());
            albumAttributeValues.forEach(albumAttributeValue -> {
                albumAttributeValueMapper.insert(albumAttributeValue);
            });
        }
        vodService.reviewAlbumText(albumInfo);
        log.info("{}，调用异步方法去进行审核内容：", Thread.currentThread().getName());
    }

    @Override
    public List<AlbumInfo> getUserAllAlbumList(Long userId) {
        LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<AlbumInfo>()
                .eq(AlbumInfo::getUserId, userId)
                .eq(AlbumInfo::getStatus, SystemConstant.ALBUM_STATUS_PASS)
                .last("LIMIT 200")
                .select(AlbumInfo::getId, AlbumInfo::getAlbumTitle)
                .orderByDesc(AlbumInfo::getId);
        List list = albumInfoMapper.selectList(lambdaQueryWrapper);

        return list;
    }

    /**
     * 根据专辑ID查询专辑统计信息
     * @param albumId
     * @return
     */
    @Override
    @GuiGuCache(prefix = RedisConstant.ALBUM_INFO_PREFIX + "stat:")
    public AlbumStatVo getAlbumStatVo(Long albumId) {
        return albumInfoMapper.getAlbumStatVo(albumId);
    }

    /**
     * 根据专辑ID查询专辑信息,利用缓存提高效率同时采用分布式锁避免缓存击穿
     * @param id
     * @return
     */
    @Override
    public AlbumInfo getAlbumInfo(Long id) {
        try {
            //优先从redis缓存中获取业务数据
            String redisKey = RedisConstant.ALBUM_INFO_PREFIX + id;
            AlbumInfo albumInfo = (AlbumInfo) redisTemplate.opsForValue().get(redisKey);
            if (albumInfo != null){
                return albumInfo;
            }

            // 如果缓存中没有数据，获取分布式锁 ：lock trylock
            String lockKey = redisKey + RedisConstant.CACHE_LOCK_SUFFIX;
            RLock lock = redissonClient.getLock(lockKey);

            //获取锁成功，查询数据，并放入缓存中，释放锁
            if (lock.tryLock()){
                try {
                    albumInfo = getAlbumInfoFromDB(id);
                    Long ttl = RedisConstant.ALBUM_TIMEOUT + RandomUtil.randomInt(50,1000);
                    redisTemplate.opsForValue().set(redisKey, albumInfo, ttl, TimeUnit.SECONDS);
                    return albumInfo;
                }finally {
                    lock.unlock();
                }
            }else {
                // 获取锁失败，自旋
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    log.error("获取锁失败");
                    throw new RuntimeException(e);
                }
                return this.getAlbumInfo(id);
            }
        } catch (Exception e) {
            //兜底方案
            log.error("查询专辑信息失败{}",e);
            return this.getAlbumInfoFromDB(id);
        }

    }
}
